﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using System.Collections;

namespace DPS.Prototype.Extension
{
    public static class ObjectExtension
    {
        private const string inspectObjectFormat = "<{0}:{1} {2} >";
        private const string inpectPropertyFormat = "@{0}=\"{1}\"";

        private static Dictionary<string, PropertyInfo[]> inspectPropertiesCache=new Dictionary<string,PropertyInfo[]>();
        public static string Inspect(this object obj)
        {
            if (obj == null)
            {
                return "";
            }
            Type type = obj.GetType();
            if (type != null)
            {
                if (type.IsAssignableFrom(typeof(IEnumerable))||type.IsSubclassOf(typeof(Array)))
                {
                    StringBuilder sbArr = new StringBuilder("[");
                    foreach (object subObj in (obj as IEnumerable))
                    {
                        sbArr.Append(subObj.Inspect());
                        sbArr.Append(",\n");
                    }
                    string arrValue = sbArr.ToString().TrimEnd(",\n".ToCharArray());
                    return arrValue + "]";
                }
                else
                {
                    PropertyInfo[] pis = FetchInspectProperties(type);
                    StringBuilder sb = new StringBuilder();
                    foreach (PropertyInfo pi in pis)
                    {
                        string value = "";
                        if (pi.PropertyType.IsDefined(typeof(InspectAttribute), false))
                        {
                            value = pi.GetValue(obj, null).Inspect();
                        }
                        else
                        {
                            value = pi.GetValue(obj, null).ToString();
                        }
                        string propertyInspectStr = string.Format(inpectPropertyFormat, pi.Name, value);
                        sb.Append(propertyInspectStr);
                        sb.Append(",");
                    }

                    string propertiesValue = sb.ToString().TrimEnd(",".ToCharArray());
                    string inspectValue = string.Format(inspectObjectFormat, type.Name, "#" + obj.GetHashCode(), propertiesValue);
                    return inspectValue;
                }
            }

            return "";
        }

        private static PropertyInfo[] FetchInspectProperties(Type type)
        {
            string key = type.FullName;
            if (!inspectPropertiesCache.ContainsKey(key)) 
            {
                lock (inspectPropertiesCache)
                {
                    if (!inspectPropertiesCache.ContainsKey(key))
                    {
                        PropertyInfo[] pis = type.GetProperties().Where(pi => pi.IsDefined(typeof(InspectAttribute), false)).ToArray();
                        inspectPropertiesCache.Add(key, pis);
                    }
                }
            }
            return inspectPropertiesCache[key];
        }
    }
}
